Skip to content

fix(cli): fix join dropping non-string x-* path extension properties#2949

Merged
Daryna-del merged 11 commits into
Redocly:mainfrom
nonchan7720:fix_issue_1574
Jul 21, 2026
Merged

fix(cli): fix join dropping non-string x-* path extension properties#2949
Daryna-del merged 11 commits into
Redocly:mainfrom
nonchan7720:fix_issue_1574

Conversation

@nonchan7720

@nonchan7720 nonchan7720 commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

What/Why/How?

Fixes join silently dropping non-string x-* extension properties defined directly under a path item (e.g.
paths./pets/{petId}.x-foo: true).

collectPaths only merged path-level extensions when their value was a string, via collectPathStringFields. Non-string values
(booleans, objects, arrays, numbers) were never copied into the joined document.

collectPathExtension now handles every non-string x-* field on a path item: it copies the value into the joined path the first
time it's seen, and logs a warning instead of silently overwriting when two source documents provide different values for the same
field.

Reference

Fixes #1574

Testing

Added a path extensions test group to join.test.ts with fixtures in documents.ts covering:

  • merging a non-string x-* path extension when both source documents agree on the value
  • logging a warning when the values conflict

Verified against the pre-fix implementation that both new tests fail without this change.

Screenshots (optional)

N/A — CLI behavior change only.

Check yourself

  • This PR follows the contributing guide
  • All new/updated code is covered by tests
  • Core code changed? - Tested with other Redocly products (internal contributions only) — N/A, packages/cli only
  • New package installed? - Tested in different environments (browser/node) — N/A
  • Documentation update has been considered — no doc change needed, this restores documented merge behavior
  • Changeset added — still missing, run npx changeset before merging (@redocly/cli, patch)

Security

  • The security impact of the change has been considered — object merge over already-parsed/trusted document data, no new external
    input surface
  • Code follows company security practices and guidelines

Note

Low Risk
Scoped CLI merge logic fix with tests; no new input surface beyond existing parsed OpenAPI documents.

Overview
Fixes join silently dropping path-level x-* extensions when values are not strings (objects, booleans, arrays, etc.).

In collect-paths.ts, path item fields are now merged when the value is a string or the key starts with x-, so those extensions are copied into the joined document like string fields (summary, description). Conflict detection uses dequal instead of !==, so object extensions with the same content merge without a warning; differing values keep the first document’s value and emit logger.warn.

Adds a path extensions test group and fixtures for matching object x-metadata, conflicting values, and objects with different keys. Includes a patch changeset for @redocly/cli.

Reviewed by Cursor Bugbot for commit 1fa9e74. Bugbot is set up for automated code reviews on this repo. Configure here.

@changeset-bot

changeset-bot Bot commented Jul 13, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 1fa9e74

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@redocly/cli Patch
@redocly/openapi-core Patch
@redocly/respect-core Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@nonchan7720 nonchan7720 changed the title fix: join extension property fix: Fix join dropping non-string x-* path extension properties. Jul 13, 2026
Clarified the fix for join extension property to specify that it addresses non-string x-* path extension properties.
@nonchan7720
nonchan7720 marked this pull request as ready for review July 13, 2026 17:16
@nonchan7720
nonchan7720 requested review from a team as code owners July 13, 2026 17:16
@nonchan7720 nonchan7720 changed the title fix: Fix join dropping non-string x-* path extension properties. fix(cli): Fix join dropping non-string x-* path extension properties. Jul 13, 2026
@github-actions

github-actions Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Performance Benchmark (Lower is Faster)

CLI Version Bundle Lint Check Config
cli-latest ▓ 1.01x ± 0.02 ▓ 1.01x ± 0.01 ▓ 1.01x ± 0.02
cli-next ▓ 1.00x (Fastest) ▓ 1.00x (Fastest) ▓ 1.00x (Fastest)

@nonchan7720 nonchan7720 changed the title fix(cli): Fix join dropping non-string x-* path extension properties. fix(cli): fix join dropping non-string x-* path extension properties. Jul 14, 2026
@nonchan7720 nonchan7720 changed the title fix(cli): fix join dropping non-string x-* path extension properties. fix(cli): fix join dropping non-string x-* path extension properties Jul 14, 2026
.mockImplementationOnce(() =>
Promise.resolve({
source: { absoluteRef: 'ref-b' },
parsed: anotherPathWithDifferentObjectExtension,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the object had different keys though? Like a.yaml has owner and b.yaml has team?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A warning log is being output.
I think we'll just have to handle this the same way we do other standard items.

If this part overlaps, it means the paths also overlap.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nonchan7720 Please add a test for this case { owner: 'team-a' } and { team: 'x' }.

@kanoru3101 kanoru3101 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the contribution. Checked it out locally — works as expected, non-string x-* path extensions now merge the same way as regular path-level fields (first one wins, warning on conflict).

Left a few comments.

Comment thread packages/cli/src/__tests__/commands/join.test.ts
Comment thread packages/cli/src/commands/join/utils/collect-paths.ts Outdated
.mockImplementationOnce(() =>
Promise.resolve({
source: { absoluteRef: 'ref-b' },
parsed: anotherPathWithDifferentObjectExtension,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nonchan7720 Please add a test for this case { owner: 'team-a' } and { team: 'x' }.

Comment thread .changeset/dull-adults-punch.md Outdated
Co-authored-by: Viktor Sydor <31951646+kanoru3101@users.noreply.github.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want higher recall? High effort reviews run extra passes and find more bugs. A team admin can switch effort levels in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 310638c. Configure here.

Comment thread packages/cli/src/commands/join/utils/collect-paths.ts Outdated

@RomanHotsiy RomanHotsiy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, left one simplification comment

if (typeof pathItem[field] === 'string') {
if (
typeof pathItem[field] === 'string' ||
(field.startsWith('x-') && typeof pathItem[field] !== 'string')

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
(field.startsWith('x-') && typeof pathItem[field] !== 'string')
(field.startsWith('x-'))

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed.
5b7a1cc

@kanoru3101 kanoru3101 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Comment thread .changeset/dull-adults-punch.md Outdated
Co-authored-by: Viktor Sydor <31951646+kanoru3101@users.noreply.github.com>
@nonchan7720

Copy link
Copy Markdown
Contributor Author

@RomanHotsiy @kanoru3101

What else do I need to do to get this PR merged? 🙏

@Daryna-del

Copy link
Copy Markdown
Contributor

Hey @nonchan7720
Many thanks for the contribution again! There is nothing left on your side, I'm merging it, and it will be released as soon as possible in the next version.

@Daryna-del
Daryna-del merged commit 7082ede into Redocly:main Jul 21, 2026
43 of 47 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

"join" remove extension properties under path property

5 participants